#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include "colors.h"
#define MIN_CONTRAST 0.5

void hsl2rgb(struct Rgb *dest, const struct Hls const *src) {
    if ((src -> h < 0 || 360 < src -> h) || 
       (src -> s < 0 && 1 < src -> s) ||
       (src -> l < 0 && 1 < src -> l)) {
        return;
    }
    float r, g, b;
    float hue = (src -> h)/60.0f;
    const float chroma = (1 - fabs((2 * (src -> l)) - 1)) * (src -> s);
    float x = chroma * (1 - fabs(fmod(hue, 2) - 1));
    float m = (src -> l) - (chroma/2);
    if (0 <= hue && hue < 1) {
        r = chroma;
        g = x;
        b = 0;
    } else if (1 <= hue && hue < 2) {
        r = x;
        g = chroma;
        b = 0;
    } else if (2 <= hue && hue < 3) {
        r = 0;
        g = chroma;
        b = x;
    } else if (3 <= hue && hue < 4) {
        r = 0;
        g = x;
        b = chroma;
    } else if (4 <= hue && hue < 5) {
        r = x;
        g = 0;
        b = chroma;
    } else if (5 <= hue && hue < 6) {
        r = chroma;
        g = 0;
        b = x;
    } else {
        r = 0;
        g = 0;
        b = 0;
    }
    dest -> r = ceil((r + m) * 255);
    dest -> g = ceil((g + m) * 255);
    dest -> b = ceil((b + m) * 255);
}

void rgb2hsl(struct Hls *dest, const struct Rgb const *src) {
    if (src == NULL || dest == NULL) {
        return;
    }
    float r = (src -> r) / 255.0f;
    float g = (src -> g) / 255.0f;
    float b = (src -> b) / 255.0f;
    float min = fmin(fmin(r, g), b);
    float max = fmax(fmax(r, g), b);
    float chroma = max - min;
    dest -> l = (max + min)/2; //bi-hexcone model.
    if (chroma == 0) { // neutral color.
        dest -> h = 0; // undefined.
        dest -> s = 0.0f;
    } else {
        dest -> s = (dest -> l > 0.5) ? (chroma/(2 - max - min)) : (chroma/(max + min));
        //piecewise function..
        float hue;
        if (max == r) {
            hue = ((g - b)/chroma) + (g < b ? 6 : 0); // % 6 will break magenta (255, 0, 255) 
        } else if (max == g) {
            hue = ((b - r)/chroma) + 2.0f;
        } else {
            hue = ((r - g)/chroma) + 4.0f;
        }
        dest -> h = (int)(hue * 60);
    }
}

static void decspan(const int d) {
    switch(d) {
        case 0:
            printf("<span style='color:rgb(128,128,128)'>");
            break;
        case 1:
            printf("<span style='color:rgb(255,0,0)'>");
            break;
        case 2:
            printf("<span style='color:rgb(51,255,0)'>");
            break;
        case 3:
            printf("<span style='color:rgb(255,255,0)'>");
            break;
        case 4:
            printf("<span style='color:rgb(51,102,255)'>");
            break;
        case 5:
            printf("<span style='color:rgb(51,255,255)'>");
            break;
        case 6:
            printf("<span style='color:rgb(255,51,102)'>");
            break;
        case 7:
            printf("<span style='color:rgb(255,255,255)'>");
            break;
        case 8:
            printf("<span style='color:rgb(153,153,153)'>");
            break;
        case 9:
            printf("<span style='color:rgb(128,128,128)'>");
            break;
    }
}

static void hexspan(const char *str) {
    const char h1[2] = {str[0], '\0'};
    const char h2[2] = {str[1], '\0'};
    const char h3[2] = {str[2], '\0'};
    struct Rgb nrgb;
    // max value for a color code in darkplaces = 0xF
    // 0xF * 0xF = 255, max value for rgb
    nrgb.r = strtol(h1, NULL, 16) * 0xF;
    nrgb.g = strtol(h2, NULL, 16) * 0xF;
    nrgb.b = strtol(h3, NULL, 16) * 0xF;
    struct Hls nhls;
    rgb2hsl(&nhls, &nrgb);
    if (nhls.l < MIN_CONTRAST) {
        nhls.l = MIN_CONTRAST;
        hsl2rgb(&nrgb, &nhls);
    }
    printf("<span style=\"color:rgb(%d,%d,%d)\">", nrgb.r, nrgb.g, nrgb.b);
}

static void b(char * const str) {
    char *token = strtok(str, "^");
    char c;
    printf("<TD>");
    while (token) {
        c = token[0];
        if (isdigit(c)) {
            decspan(c - '0');
            if (strlen(token) > 1) {
                printf("%s", token + 1);
            }
            printf("</span>");
        } else if ((c == 'x' && strlen(token) > 3) && 
       (isxdigit(token[1]) && 
        isxdigit(token[2]) && 
        isxdigit(token[3]))) {
            hexspan(token + 1); //exclude x
            if (strlen(token) > 4){
                printf("%s", token + 4);
            }
            printf("</span>");
        } else {
            printf("%s", token);
        }
        token = strtok(NULL, "^");
    }
    printf("</TD>");
}

void print_plname(const char* str) {
    //find instance of ^^
    //replace with ^
    //concatenate with rest of the string
    char *copy;
    copy = calloc(strlen(str) + 1, sizeof(char));
    strcpy(copy, str);
    char *pos = copy;
    while (pos = strstr(pos, "^^")) {
        strcpy(pos, (pos + 1));
    }
    b(copy);
    free(copy);
}

